use std::{collections::HashMap, sync::Arc};

use monitor::{CompleterSuggestion, Monitor, MonitorCommand};

use super::fdinfo::{fdinfo_get, mon_fd_completer};
use crate::{DSeekFrom, DfsError, DfsResult};

struct SeekCommand;

impl SeekCommand {
    fn do_seek(&self, args: &HashMap<String, String>) -> DfsResult<u64> {
        let fd = args.get("fd").unwrap().parse::<u64>().map_err(|_| DfsError::InvalidArgs)?;
        let file = fdinfo_get(fd)?;
        let from = args.get("from").unwrap().as_str();
        let pos = args.get("pos").unwrap().parse::<i64>().map_err(|_| DfsError::InvalidArgs)?;
        let ds = match from {
            "s" => DSeekFrom::Start(pos as u64),
            "c" => DSeekFrom::Current(pos),
            "e" => DSeekFrom::End(pos),
            _ => return Err(DfsError::InvalidArgs),
        };
        file.seek(ds)
    }
}

impl MonitorCommand for SeekCommand {
    fn mon_protocol_exec(&self, args: &monitor::Protocol) -> DfsResult<monitor::Protocol> {
        let size = self.do_seek(args)?;
        let mut hash = HashMap::new();
        hash.insert("size".to_string(), size.to_string());
        Ok(hash)
    }

    fn mon_readline_arg_type(&self) -> Option<&'static str> {
        Some("fd:u64,from:str,pos:i64")
    }
    fn mon_readline_completer(&self, args: &[&str], is_space: bool) -> Option<CompleterSuggestion> {
        mon_fd_completer(args, is_space)
    }

    fn mon_readline_exec(
        &self,
        args: &HashMap<String, String>,
        printer: &monitor::MonitorPrinter,
    ) -> DfsResult<()> {
        let size = self.do_seek(args)?;
        printer.print(size.to_string());
        Ok(())
    }

    fn mon_readline_help(&self) -> String {
        String::from("调整文件指针位置")
    }

    fn mon_readline_long_help(&self) -> Option<String> {
        Some(String::from(
            "seek <fd> <s|c|e> <pos>
根据 s(文件起始)/c(文件当前位置)/e(文件结束位置) 调整 pos, 并且返回调整后 pos",
        ))
    }
}

pub(crate) fn monitor_command_seek_register(mon: &Monitor) {
    mon.register_command("seek", Arc::new(SeekCommand)).unwrap();
}
